using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Markup;
using Rhizome.Contract;
using Rhizome.Contract.Diag;
using Rhizome.Contract.Xaml;
using Rhizome.Internal;
using Rhizome.Model.Validation;

namespace Rhizome.Model
{
	/// <summary>
	/// Sets the Value to the Property (or Binding)
	/// </summary>
	/// <typeparam name="TValue">Property and Value type</typeparam>
	[ContentProperty("Value")]
	public class Setter<TValue> : PropertyActionBase, IInitFromString
	{
		private TValue _value;
		private bool _isValuePresent;

		/// <summary>
		/// Value to set
		/// </summary>
		public TValue Value
		{
			[DebuggerStepThrough]
			get { return _value; }
			[DebuggerStepThrough]
			set
			{
				_value = value;
				_isValuePresent = true;
			}
		}

		public override void Process()
		{
			Binding.Value = Value;
		}

		#region Overrides of PropertyActionBase

		public override IEnumerable<IValidationResult> GetValidationResults()
		{
			return
				_isValuePresent
					? base.GetValidationResults()
					: base.GetValidationResults().Concat(new[] {ValidationResult.ValueNotSetWarning});
		}

		#endregion

		#region Implementation of IInitFromString

		public void Init(string s)
		{
			if (s.IsNullOrEmpty()) throw new ArgumentNullException("s");
			
			var pair = s.Split('=');
			if (pair.Length !=2 ) throw new InvalidOperationException("Invalid syntax");
			
			Property = pair[0].Trim();
			
			var val = pair[1].Trim();
			Value = val.ChangeType<TValue>();
		}

		#endregion
	}

	/// <summary>
	/// Sets specific Value to specific Property
	/// </summary>
	public sealed class Setter : Setter<object>
	{
		private bool _isValueCached;
		
		#region Overrides of Setter<object>

		public override void Process()
		{
			if (_isValueCached == false)
			{
				Value = Value.ChangeType(Binding.Type);
				_isValueCached = true;
			}

			Binding.Value = Value;
		}

		#endregion
	}

	public sealed class SetString : Setter<string>
	{
	}

	public sealed class SetBool : Setter<bool>
	{
	}

	public sealed class SetInt : Setter<int>
	{
	}

	public sealed class SetDouble : Setter<double>
	{
	}

	public sealed class SetDecimal : Setter<decimal>
	{
	}

	public sealed class SetDate : Setter<DateTime>
	{
	}

	public sealed class SetTimeStamp : Setter<TimeSpan>
	{
	}
}